package com.ly.thread.problem;
import java.util.concurrent.atomic.AtomicReference;
import	java.util.concurrent.locks.Condition;
import	java.util.concurrent.locks.ReentrantLock;

import java.util.concurrent.Semaphore;

/**
 * @Classname FooBar
 * @Description
 * 我们提供一个类：
 *
 * class FooBar {
 *   public void foo() {
 *     for (int i = 0; i < n; i++) {
 *       print("foo");
 *     }
 *   }
 *
 *   public void bar() {
 *     for (int i = 0; i < n; i++) {
 *       print("bar");
 *     }
 *   }
 * }
 * 两个不同的线程将会共用一个 FooBar 实例。其中一个线程将会调用 foo() 方法，另一个线程将会调用 bar() 方法。
 *
 * 请设计修改程序，以确保 "foobar" 被输出 n 次。
 *
 *  
 *
 * 示例 1.txt:
 *
 * 输入: n = 1.txt
 * 输出: "foobar"
 * 解释: 这里有两个线程被异步启动。其中一个调用 foo() 方法, 另一个调用 bar() 方法，"foobar" 将被输出一次。
 * 示例 2:
 *
 * 输入: n = 2
 * 输出: "foobarfoobar"
 * 解释: "foobar" 将被输出两次。
 *
 * @Date 2020/6/10 9:43
 * @Author 冷心影翼
 */
class FooBar {
    private int n;

    private Semaphore foo = new Semaphore(1);
    private Semaphore bar = new Semaphore(0);

    public FooBar(int n) {
        this.n = n;
    }

    public void foo(Runnable printFoo) throws InterruptedException {

        for (int i = 0; i < n; i++) {
            foo.acquire();
            // printFoo.run() outputs "foo". Do not change or remove this line.
            printFoo.run();
            bar.release();
        }
    }

    public void bar(Runnable printBar) throws InterruptedException {

        for (int i = 0; i < n; i++) {
            bar.acquire();
            // printBar.run() outputs "bar". Do not change or remove this line.
            printBar.run();
            foo.release();
        }
    }
}



class FooBar2 {
    private int n;

    private boolean flag = true;

    public FooBar2(int n) {
        this.n = n;
    }

    public synchronized void foo(Runnable printFoo) throws InterruptedException {
        for (int i = 0; i < n; i++) {
            // printFoo.run() outputs "foo". Do not change or remove this line.
            if(!flag) {
                wait();
            }
            printFoo.run();
            flag = false;
            notifyAll();
        }
    }

    public synchronized void bar(Runnable printBar) throws InterruptedException {

        for (int i = 0; i < n; i++) {
            // printBar.run() outputs "bar". Do not change or remove this line.
            if(flag) {
                wait();
            }
            printBar.run();
            flag = true;
            notifyAll();
        }
    }

}


class FooBar3 {
    private int n;

    private boolean flag = true;

    private ReentrantLock lock = new ReentrantLock();

    private Condition condition = lock.newCondition();

    public FooBar3(int n) {
        this.n = n;
    }

    public void foo(Runnable printFoo) throws InterruptedException {
        lock.lock();
        try{
            for (int i = 0; i < n; i++) {
                // printFoo.run() outputs "foo". Do not change or remove this line.
                while(!flag) {
                    condition.await();
                }
                printFoo.run();
                flag = false;
                condition.signal();
            }
        }finally {
            lock.unlock();
        }
    }

    public  void bar(Runnable printBar) throws InterruptedException {
        lock.lock();
        try{
            for (int i = 0; i < n; i++) {
                // printBar.run() outputs "bar". Do not change or remove this line.
                while (flag) {
                    condition.await();
                }
                printBar.run();
                flag = true;
                condition.signal();
            }
        }finally {
            lock.unlock();
        }


    }

}


class Ticket {
    private  AtomicReference<Integer> atomicReference = new AtomicReference<Integer> ();

    Ticket() {
        atomicReference.set(2000000);
    }

    public AtomicReference<Integer> getAtomicReference() {
        return atomicReference;
    }

    public void setAtomicReference(AtomicReference<Integer> atomicReference) {
        this.atomicReference = atomicReference;
    }
}

class FooBarTest {
    public static void main(String[] args) throws InterruptedException {

//        FooBar3 fooBar3 = new FooBar3(10);
//        new Thread(()-> {
//            try {
//                fooBar3.foo(()-> {
//                    System.out.print("foo");
//                });
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//        }).start();
//        new Thread(()-> {
//            try {
//                fooBar3.bar(()-> {
//                    System.out.print("bar");
//                });
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//        }).start();


    }
}